using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using Newtonsoft.Json.Serialization;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESGRatings.DataContract.ESG;



using Xunit;
using Xunit.Abstractions;

namespace Sustainalytics.ESGComputing.Service.Tests
{
    public class ESGClientComputationValidationTest : Disposable, IClassFixture<ComputingFixture>
    {
        private static readonly string _sutLink = "http://localhost:10280/esgcomputingserv/api/v1/esgcompute";

        private static readonly JsonMediaTypeFormatter JsonMediaTypeFormatter = new JsonMediaTypeFormatter
        {
            SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
        };

        private readonly ComputingFixture _dataFixture;
        private readonly ITestOutputHelper _output;
        private readonly HttpClient _sutClient;
        private bool _disposed;

        public ESGClientComputationValidationTest(ComputingFixture fixture, ITestOutputHelper output)
        {
            _dataFixture = fixture;
            _output = output;
            _sutClient = new HttpClient();
            _sutClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        ~ESGClientComputationValidationTest()
        {
            Dispose(false);
        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_Data_For_One_Company_In_Options_With_ClusterRatings_Below_Zero()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                CompaniesIds = new List<Guid> { _dataFixture.Companies[0].Id },
                IncludeAggregatedScores = true,
                IncludeIndWeightedScores = true,
                IncludeRatings = true,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());
            _output.WriteLine(esgRatingsDto.Result.ToString());

            //Assert

            Assert.True(esgRatingsDto.Result.Ratings.Where(x => x.CompanyId == _dataFixture.Companies[0].Id).SelectMany(x=>x.ClustersRatings).All(x=>x<0));
        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_TopCompanies_For_OneCompany()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                CompaniesIds = new List<Guid> { _dataFixture.Companies[0].Id },
                IncludeTopPerformersInfo = true,
                IncludeAggregatedScores = true,
                IncludeIndWeightedScores = true,
                IncludeRatings = true,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());

            _output.WriteLine(esgRatingsDto.Result.ToString());


            

            //Assert
        
            var companyRatingDto = esgRatingsDto.Result.Ratings.FirstOrDefault(x => x.CompanyId == _dataFixture.Companies[0].Id);

            Assert.NotNull(companyRatingDto);
            Assert.True(
                esgRatingsDto.Result.Groups.Where(x => x.GroupId == companyRatingDto.GroupId)
                    .All(x => x.TopCompaniesScoresOnComparativeGroup.Count(inner => inner.Index == 0) <= 5));
        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_AtMost_5_TopCompanies_For_AllCluster_For_AllCompanies()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                IncludeTopPerformersInfo = true,
                IncludeAggregatedScores = true,
                IncludeIndWeightedScores = true,
                IncludeRatings = true,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());

            _output.WriteLine(esgRatingsDto.Result.ToString());


            //Assert
          

            Assert.True(
                esgRatingsDto.Result.Groups
                    .All(x => x.TopCompaniesScoresOnComparativeGroup.Count(inner => inner.Index == 0) <= 5));
        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_Type_B_Companies_With_Score_More_Than_Zero()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                TemplateType = TemplateTypeEnum.TypeB,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());
           
           var outputCollection=esgRatingsDto.Result.Ratings.Select(x => new { x.CompanyId, x.ClustersAggregates[0].Rank, x.ClustersAggregates[0].Count });

           _output.WriteLine(outputCollection.Dump());

            //Assert
            
            var expected = (from company in _dataFixture.Companies.Where(x => string.Equals(x.TemplateType, "Type B", StringComparison.InvariantCultureIgnoreCase))
                               join cweight in _dataFixture.Matrices[0].CompaniesWeights on company.Id equals cweight.CompanyId
                               where cweight.ProfileHasEsgResearch
                               orderby company.Id
                               select company.Id);

            _output.WriteLine(expected.Dump());

            var actual = esgRatingsDto.Result.Ratings.Where(x => x.ClustersAggregates.All(r=>r.Rank > 0)).Select(x=>x.CompanyId).OrderBy(x=>x);
            Assert.Equal(expected,actual);

        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_Type_A_and_Private_Companies_With_Score_More_Than_Zero()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                TemplateType = TemplateTypeEnum.TypeA,
                CompanyType=CompanyTypeEnum.Private,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());

            var outputCollection = esgRatingsDto.Result.Ratings.Select(x => new { x.CompanyId, x.ClustersAggregates[0].Rank, x.ClustersAggregates[0].Count });

            _output.WriteLine(outputCollection.Dump());

            //Assert

            var expected = (from company in _dataFixture.Companies.Where(x => string.Equals(x.TemplateType, "Type A", StringComparison.InvariantCultureIgnoreCase) && string.Equals(x.CompanyType, "Private", StringComparison.InvariantCultureIgnoreCase))
                            join cweight in _dataFixture.Matrices[0].CompaniesWeights on company.Id equals cweight.CompanyId
                            where cweight.ProfileHasEsgResearch
                            orderby company.Id
                            select company.Id);

            _output.WriteLine(expected.Dump());

            var actual = esgRatingsDto.Result.Ratings.Where(x => x.ClustersAggregates.All(r => r.Rank > 0)).Select(x => x.CompanyId).OrderBy(x => x);
            Assert.Equal(expected, actual);

        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_Type_A_and_Public_Companies_With_Score_More_Than_Zero()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                TemplateType = TemplateTypeEnum.TypeA,
                CompanyType = CompanyTypeEnum.Public,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());

            var outputCollection = esgRatingsDto.Result.Ratings.Select(x => new { x.CompanyId, x.ClustersAggregates[0].Rank, x.ClustersAggregates[0].Count });

            _output.WriteLine(outputCollection.Dump());

            //Assert

            var expected = (from company in _dataFixture.Companies.Where(x => string.Equals(x.TemplateType, "Type A", StringComparison.InvariantCultureIgnoreCase) && string.Equals(x.CompanyType, "Public", StringComparison.InvariantCultureIgnoreCase))
                            join cweight in _dataFixture.Matrices[0].CompaniesWeights on company.Id equals cweight.CompanyId
                            where cweight.ProfileHasEsgResearch
                            orderby company.Id
                            select company.Id);

            _output.WriteLine(expected.Dump());

            var actual = esgRatingsDto.Result.Ratings.Where(x => x.ClustersAggregates.All(r => r.Rank > 0)).Select(x => x.CompanyId).OrderBy(x => x);
            Assert.Equal(expected, actual);

        }

        [Fact]
        public void ESGComputeController_Post_Should_Return_Combined_Type_and_Combined_Template_Companies_With_Score_More_Than_Zero()
        {
            //Arrange

            var options = new ESGRatingsOptionsDto()
            {
                RefUnivId = _dataFixture.UniverseId,
                MatrixId = _dataFixture.MatrixId,
                TemplateType = TemplateTypeEnum.Combined,
                CompanyType = CompanyTypeEnum.Combined,
                GroupBy = ComparativeGroupEnum.PeerGroup,
            };

            //Act
            var result = _sutClient.PostAsync(_sutLink, options, JsonMediaTypeFormatter);

            var esgRatingsDto = result.Result.Content.ReadAsAsync<ESGRatingsDto>(JsonMediaTypeFormatter.Yield());

            var outputCollection = esgRatingsDto.Result.Ratings.Select(x => new { x.CompanyId, x.ClustersAggregates[0].Rank, x.ClustersAggregates[0].Count });

            _output.WriteLine(outputCollection.Dump());

            //Assert

            var expected = (from company in _dataFixture.Companies
                            join cweight in _dataFixture.Matrices[0].CompaniesWeights on company.Id equals cweight.CompanyId
                            where cweight.ProfileHasEsgResearch
                            orderby company.Id
                            select company.Id);

            _output.WriteLine(expected.Dump());

            var actual = esgRatingsDto.Result.Ratings.Where(x => x.ClustersAggregates.All(r => r.Rank > 0)).Select(x => x.CompanyId).OrderBy(x => x);
            Assert.Equal(expected, actual);

        }

        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _sutClient.Dispose();

                }

                _disposed = true;
            }

            base.Dispose(disposing);
        }
    }
}